
(function($) {

$.widget('ui.tail', {
	context : null,
	show : function() {
		//Y.log("show tail begin");
			
		//this.context.clearRect(0,0,$(this.element).outerWidth(true)+100, $(this.element).outerHeight(true)+100);
		
		//fromRect.left = from.position().left;
		//fromRect.top = from.position().top;
		//fromRect.right = fromRect.left + from.outerWidth(true);
		//fromRect.bottom = fromRect.top + from.outerHeight(true);
		var options = this.options;
		var from = options.from;
		var to = options.isDialog ? options.to.parent() : options.to;
			
		var direct = '';
		
		var fromRect = {
			left : from.position().left,
			top : from.position().top,
			right : from.position().left + from.outerWidth(true),
			bottom : from.position().top + from.outerHeight(true)
		}, toRect = {
		}, canvasRect = {
		}, orig_triangle = {
		}, to_triangle = {
		};
		
		toRect.left = to.position().left;
		toRect.top = to.position().top;
		toRect.right = toRect.left + to.outerWidth(true) - 1;
		toRect.bottom = toRect.top + to.outerHeight(true) - 1;
		
		function calcOrigTriangle() {
			if (direct==='v') {
				orig_triangle.x0 = fromRect.left + from.outerWidth(true) / 2;
				orig_triangle.x2 = toRect.left + to.outerWidth(true) / 2 - options.offset;
				orig_triangle.x3 = toRect.left + to.outerWidth(true) / 2 + options.offset;
			} else {
				orig_triangle.y0 = fromRect.top + from.outerHeight(true) / 2;
				orig_triangle.y2 = toRect.top + to.outerHeight(true) / 2 - options.offset;
				orig_triangle.y3 = toRect.top + to.outerHeight(true) / 2 + options.offset;
			}
		};
			
		////Y.log("from rect : " + Y.dump(fromRect));
		////Y.log("to rect : " + Y.dump(toRect));
		function calcCanvasRect() {
			canvasRect.left = Math.min(orig_triangle.x0, orig_triangle.x2, orig_triangle.x3);
			canvasRect.top = Math.min(orig_triangle.y0, orig_triangle.y2, orig_triangle.y3);
			canvasRect.right = Math.max(orig_triangle.x0, orig_triangle.x2, orig_triangle.x3);
			canvasRect.bottom = Math.max(orig_triangle.y0, orig_triangle.y2, orig_triangle.y3);
			canvasRect.width = canvasRect.right - canvasRect.left + 1;
			canvasRect.height = canvasRect.bottom - canvasRect.top + 1;
		};
		
		function calcToTriangle() {
			calcOrigTriangle();
			calcCanvasRect();
			if (direct==='v') {
				if (canvasRect.width > canvasRect.height * 2) {
					//direct==='h';
				}
				to_triangle.x0 = orig_triangle.x0 - canvasRect.left;
				to_triangle.x2 = orig_triangle.x2 - canvasRect.left;
				to_triangle.x3 = orig_triangle.x3 - canvasRect.left;
			} else {
				if (canvasRect.height > canvasRect.width * 2) {
					//direct==='v';
				}
				to_triangle.y0 = orig_triangle.y0 - canvasRect.top;
				to_triangle.y2 = orig_triangle.y2 - canvasRect.top;
				to_triangle.y3 = orig_triangle.y3 - canvasRect.top;
			}
		};
		
		if (toRect.left - fromRect.right > options.range) {
			//Y.log("from at left, to at right");
			direct = 'h';
			orig_triangle.x0 = fromRect.right;
			orig_triangle.x2 = orig_triangle.x3 = toRect.left;
			
			calcToTriangle();
			
			to_triangle.x0 = 0;
			to_triangle.x2 = to_triangle.x3 = canvasRect.width-0;
		}
		else if (fromRect.left - toRect.right > options.range) {
			//Y.log("from at right, to at left");
			direct = 'h';
			
			orig_triangle.x0 = fromRect.left;
			orig_triangle.x2 = orig_triangle.x3 = toRect.right;
			
			calcToTriangle();
			
			to_triangle.x0 = canvasRect.width-0;
			to_triangle.x2 = to_triangle.x3 = 0;
		}
		else if (toRect.top - fromRect.bottom > options.range) {
			//Y.log("from at top, to at bottom");
			direct = 'v';
			orig_triangle.y0 = fromRect.bottom;
			orig_triangle.y2 = orig_triangle.y3 = toRect.top;
			
			calcToTriangle();
			
			to_triangle.y0 = -0;
			to_triangle.y2 = to_triangle.y3 = canvasRect.height-0;
		}
		else if (fromRect.top - toRect.bottom > options.range) {
			//Y.log("from at bottom, to at top");
			direct = 'v';
			orig_triangle.y0 = fromRect.top;
			orig_triangle.y2 = orig_triangle.y3 = toRect.bottom;
			
			calcToTriangle();
			
			to_triangle.y0 = canvasRect.height-0;
			to_triangle.y2 = to_triangle.y3 = 0;
		}
		
		if (direct !== '') {
			//Y.log("canvas rect : " + Y.dump(canvasRect));
			//Y.log("orig_triangle : " + Y.dump(orig_triangle));
			//Y.log("to_triangle : " + Y.dump(to_triangle));
			
			this.element.attr("width", canvasRect.width).attr("height", canvasRect.height);
			this.element.css({"left": canvasRect.left, "top":canvasRect.top, "z-index": to.css("z-index"), "display": ""});//.width(canvasRect.width).height(canvasRect.height);
			
			
			this.context.beginPath();
			this.context.fillStyle   = options.fillStyle;
			this.context.strokeStyle = options.strokeStyle;
			this.context.lineWidth   = 1;
			this.context.moveTo(to_triangle.x0, to_triangle.y0);
			this.context.lineTo(to_triangle.x2, to_triangle.y2);
			this.context.lineTo(to_triangle.x3, to_triangle.y3);
			this.context.lineTo(to_triangle.x0, to_triangle.y0);
			this.context.fill();
			this.context.stroke();
			this.context.closePath();
			
			this.context.beginPath();
			this.context.strokeStyle = options.fillStyle;
			this.context.lineWidth   = 1;
			this.context.moveTo(to_triangle.x2 + (direct === 'v' ? 1 : 0), to_triangle.y2 + (direct === 'h' ? 1 : 0));
			this.context.lineTo(to_triangle.x3 + (direct === 'v' ? -1 : 0), to_triangle.y3 + (direct === 'h' ? -1 : 0));
			this.context.stroke();
			this.context.closePath();
		}
		//Y.log("show tail end");
	},
	hide : function() {
		//Y.log("hide tail");
		this.context.clearRect(0,0,this.element.outerWidth(true)+100, this.element.outerHeight(true)+100);
		this.element.css("display", "none");
	},
	_init: function(){ 
		//Y.log("init tail begin");
		this.element.css({"position" : "absolute", "background":"none"});//, "z-index": 10000, "border" : "1px #000000 solid"});
		
		////Y.log (Y.dump(this));
		if (!this.element.get(0).getContext) {
			//Y.log("browser not support canvas");
			return;
		}
		
		this.context = this.element.get(0).getContext('2d');
		
		var self = this; 
		/*
		var from = this.options.from;
		from.open(function(event, ui) {
			self.open();
		}).dragStart(function(event, ui) {
			self.hide();
		}).dragStop(function(event, ui) {
			self.open();
		}).hide(function(event, ui) {
			self.hide();
		});*/
		////Y.log (Y.dump(this.options.to));
		if (this.options.isDialog) {
			var to = this.options.to;
			to.bind('dialogopen', function(event, ui) {
				self.show();
			}).bind('dialogclose', function(event, ui) {
				self.hide();
			}).dialog('option', 'dragStart', function(event, ui) {
				self.hide();
			}).dialog('option', 'dragStop', function(event, ui) {
				self.show();
			});
		}
		
		(this.options.autoShow && this.show());
		
		//Y.log("init tail end");
	}
});

$.extend($.ui.tail, {
	cssFiles:{},
	styles : {},
	defaults: {
		autoShow: false,
		isDialog: true,
		from : null,
		to : null,
		strokeStyle : "#A6C9E2",
		fillStyle : "#ffffff",
		offset : 15,
		range : 20
	}, setDefaults : function(settings) {
		this.defaults = $.extend(this.defaults, settings);
		//Y.log("setting defaults options: " + Y.dump(this.defaults));
	}, setStyles: function(url) {
		Y.log("$.ui.tail set styles from url " + url);
		if (this.styles[url]) {
			this.setDefaults({strokeStyle:this.styles[url].strokeStyle, 
				fillStyle:this.styles[url].fillStyle});
			return;
		}
			
		var classes = this.findCssClass(url, '.ui-widget-content');
		var strokeCss = classes['.ui-widget-content'];
		
		if (strokeCss) {
			//Y.log("analyse stroke css " + strokeCss);
			//var borderRegex = /border[^#]*(#[\w]+);[\s]*background[^#]*(#[\w]+)/gm;
			styles = strokeCss.split("#");
			//var styles = borderRegex.exec(strokeCss);
			var strokeStyle = '#'+styles[1].substring(0, styles[1].indexOf(";"));
			var fillStyle = '#'+styles[2].substring(0, styles[2].indexOf(" "));
			this.styles[url] = {strokeStyle:strokeStyle, fillStyle:fillStyle};
			//Y.log("change strokeStyle to '" + strokeStyle + "'-'" + fillStyle + "'");
			this.setDefaults({strokeStyle:strokeStyle, fillStyle:fillStyle});
		}
	}, findCssClass : function(url, cssClassNames){
		var result = {
		};
		
		if (cssClassNames && typeof(cssClassNames)==='string') {
			cssClassNames=[cssClassNames];
		}
		
		function find(url) {
			if (!cssClassNames) {
				return;
			}
			
			var text = $.ui.tail.cssFiles[url] ? $.ui.tail.cssFiles[url] : $.ui.tail.cssFiles[url] =
				$.ajax({
					url: url,
					async: false
				}).responseText;
			//Y.log("get css from "+url);
			//Y.log(text);
				
			for (i = 0; i < cssClassNames.length; i++) {
				if (!cssClassNames[i])
					continue;
					
				var arr = null;
				
				var regex = new RegExp(cssClassNames[i]+"[\\s]*[,]?[^\\{]+\\{[\\s]*([^\\n]+)[\\s]*\\}", "gm");
				if (arr = regex.exec(text)) {
					Y.log('find class "'+cssClassNames[i]+'" = "' + arr[1] + '"');
					result[cssClassNames[i]] = arr[1];
					cssClassNames[i] = null;
				} else {
					regex = /@import[\s]+url\([\s]*"([^;]+)"[\s]*\)[\s]*;/gm;
					while (arr = regex.exec(text)){//arr = text.match(regex)) {
						//Y.log('find import = ' + arr[1]);
						var path = url.substring(0, url.lastIndexOf("/")+1);
						find(path + arr[1]);
					}
				}
			}
		}
		
		find(url);
		
		return result;
	}
});

})(jQuery);